package net.wwang.blog.utils;

import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.reflect.ClassPath;
import com.google.common.reflect.ClassPath.ClassInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.DigestUtils;
import org.springframework.util.ResourceUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.UnknownHostException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author WangWei
 * @date 2020/8/14
 */
@Slf4j
public class Utils {
	private Utils() {
		super();
	}

	private static final Base64.Decoder DECODER = Base64.getDecoder();

	private static final Base64.Encoder ENCODER = Base64.getEncoder();

	public static boolean isEmpty(String str) {
		return str == null || str.length() < 1 || "null".equals(str);
	}

	public static boolean isNotEmpty(String str) {
		return !isEmpty(str);
	}

	public static boolean isEmpty(List<?> list) {
		return list == null || list.size() < 1;
	}

	public static boolean isNotEmpty(List<?> list) {
		return !isEmpty(list);
	}

	public static boolean isEmpty(Map<?, ?> map) {
		return map == null || map.size() < 1;
	}

	public static boolean isNotEmpty(Map<?, ?> map) {
		return !isEmpty(map);
	}

	public static boolean isEmpty(Object[] list) {
		return list == null || list.length < 1;
	}

	public static boolean isNotEmpty(Object[] list) {
		return !isEmpty(list);
	}

	public static Pattern numberPattern = Pattern.compile("[0-9]*");

	public static List<String> stringToList(String str) {
		List<String> strs = new ArrayList<String>();

		if (Utils.isNotEmpty(str) && str.contains(",")) {
			strs = Arrays.asList(str.split(","));
		} else {
			strs.add(str);
		}

		return strs;
	}

	/**
	 * 将对象根据路径写入指定的xml文件里
	 */
	public static void convertToXml(Object obj, String path) {
		try {
			// 利用jdk中自带的转换类实现
			JAXBContext context = JAXBContext.newInstance(obj.getClass());

			Marshaller marshaller = context.createMarshaller();
			// 格式化xml输出的格式
			marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
					Boolean.TRUE);
			marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");
			// 将对象转换成输出流形式的xml
			// 创建输出流
			FileWriter fw = null;
			try {
				fw = new FileWriter(path);
			} catch (IOException e) {
				e.printStackTrace();
			}
			marshaller.marshal(obj, fw);
		} catch (JAXBException e) {
			e.printStackTrace();
		}
	}

	/**
	 * String数组转Integer
	 *
	 * @param strings
	 * @return
	 */
	public static Integer[] convertStringToInteger(String[] strings) {
		if (Utils.isEmpty(strings)) {
			return null;
		}
		Integer[] integers = new Integer[strings.length];
		for (int i = 0; i < strings.length; i++) {
			integers[i] = Integer.valueOf(strings[i]);
		}
		return integers;
	}


	public static void sleep(int millSeconds) {
		try {
			Thread.sleep(millSeconds);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 截图转成文件
	 *
	 * @param fileName
	 */
	public static void generateScreenShotToFile(String fileName) {
		Rectangle screenRect = new Rectangle(Toolkit.getDefaultToolkit().getScreenSize());
		BufferedImage capture;
		try {
			capture = new Robot().createScreenCapture(screenRect);
			ImageIO.write(capture, "jpg", new File(fileName));
		} catch (IOException | AWTException e) {
			e.printStackTrace();
		}
	}


	/**
	 * 获取SHA256编码串
	 *
	 * @param str
	 * @return
	 */
	public static String getSHA256Str(String str) {
		MessageDigest messageDigest;
		String encdeStr = "";
		try {
			messageDigest = MessageDigest.getInstance("SHA-256");
			byte[] hash = messageDigest.digest(str.getBytes("UTF-8"));
			encdeStr = Hex.encodeHexString(hash);
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (UnsupportedEncodingException e1) {
			e1.printStackTrace();
		}
		return encdeStr;
	}

	/**
	 * 获取MD5盐值
	 *
	 * @param key
	 * @return
	 */
	public static String getMd5Value(String key) {
		return DigestUtils.md5DigestAsHex(key.getBytes());
	}


	/**
	 * 获取包内所有类
	 *
	 * @param packagePath
	 * @param isRecursived
	 * @param isSimpleName
	 * @return
	 */
	public static List<String> getClassOfPackage(String packagePath, boolean isRecursived,
	                                             boolean isSimpleName) {
		final ClassLoader loader = Thread.currentThread().getContextClassLoader();
		List<String> classes = Lists.newArrayList();
		try {
			ClassPath classpath = ClassPath.from(loader);
			ImmutableSet<ClassInfo> classInfos;

			if (isRecursived) {
				classInfos = classpath.getTopLevelClassesRecursive(packagePath);
			} else {
				classInfos = classpath.getTopLevelClasses(packagePath);
			}

			for (ClassInfo classInfo : classInfos) {
				if (classInfo.getSimpleName().endsWith("_")) {
					continue;
				}

				String classFullName = classInfo.getName();
				if (isSimpleName) {
					classFullName = classInfo.getSimpleName();
				}

				classes.add(classFullName);
				log.info("Found class: " + classFullName);
			}
		} catch (IOException e) {
			log.error("Cannot get class list from package: " + packagePath);
			e.printStackTrace();
		}

		return classes;
	}

	/**
	 * 获取用户真实IP地址，不使用request.getRemoteAddr();的原因是有可能用户使用了代理软件方式避免真实IP地址。
	 * 可是，如果通过了多级反向代理的话，X-Forwarded-For的值并不止一个，而是一串IP值，究竟哪个才是真正的用户端的真实IP呢？
	 * 答案是取X-Forwarded-For中第一个非unknown的有效IP字符串
	 *
	 * @param request
	 * @return
	 */
	public static String getIpAddress(HttpServletRequest request) {
		String ip = request.getHeader("x-forwarded-for");

		if ((ip == null) || (ip.length() == 0) ||
				"unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("Proxy-Client-IP");
		}

		if ((ip == null) || (ip.length() == 0) ||
				"unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("WL-Proxy-Client-IP");
		}

		if ((ip == null) || (ip.length() == 0) ||
				"unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_CLIENT_IP");
		}

		if ((ip == null) || (ip.length() == 0) ||
				"unknown".equalsIgnoreCase(ip)) {
			ip = request.getHeader("HTTP_X_FORWARDED_FOR");
		}

		if ((ip == null) || (ip.length() == 0) ||
				"unknown".equalsIgnoreCase(ip)) {
			ip = request.getRemoteAddr();

			if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) {
				//根据网卡取本机配置的IP
				InetAddress inet = null;

				try {
					inet = InetAddress.getLocalHost();
				} catch (UnknownHostException e) {
					e.printStackTrace();
				}

				ip = inet.getHostAddress();
			}
		}

		return ip;
	}

	/**
	 * 获取本地IP地址
	 *
	 * @return
	 */
	public static String getLocalIp() {
		String ipAddress = "127.0.0.1";
		InetAddress inetAddress = null;
		try {
			inetAddress = InetAddress.getLocalHost();
			ipAddress = inetAddress.getHostAddress();
		} catch (UnknownHostException e) {
			try {
				ipAddress = getPublicIPv4();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
		}
		return ipAddress;
	}

	/**
	 * 获取广域网IPV4地址
	 *
	 * @return
	 * @throws Exception
	 */
	private static String getPublicIPv4() throws Exception {
		Enumeration<NetworkInterface> e = NetworkInterface.getNetworkInterfaces();
		String ipToReturn = null;

		while (e.hasMoreElements()) {
			NetworkInterface n = e.nextElement();
			Enumeration<InetAddress> ee = n.getInetAddresses();
			while (ee.hasMoreElements()) {
				InetAddress i = ee.nextElement();
				String currentAddress = i.getHostAddress();
				if (validate(currentAddress) && i.isReachable(1000)) {
					ipToReturn = currentAddress;
				}
			}
		}

		return ipToReturn;
	}

	/**
	 * 判断是否为局域网IP
	 *
	 * @param hostAddr
	 * @return
	 */
	private static boolean validate(final String hostAddr) {
		if (hostAddr.indexOf("192.") == 0 || hostAddr.indexOf("10.") == 0
				|| hostAddr.indexOf("172.") == 0) {
			return true;
		}
		return false;
	}

	public static String hashString(String s) {
		byte[] hash = null;
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-256");
			hash = md.digest(s.getBytes());
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}

		StringBuilder sb = new StringBuilder();
		for (int i = 0; i < hash.length; ++i) {
			String hex = Integer.toHexString(hash[i]);
			if (hex.length() == 1) {
				sb.append(0);
				sb.append(hex.charAt(hex.length() - 1));
			} else {
				sb.append(hex.substring(hex.length() - 2));
			}
		}

		return sb.toString();
	}

	public static String toString(int[] s) {
		StringBuilder authorityString = new StringBuilder();
		for (int i : s) {
			authorityString.append(i);
			authorityString.append(",");
		}
		String sb = authorityString.toString();
		return sb.substring(0, sb.length() - 1);
	}

	/**
	 * 转换为Html编码
	 *
	 * @param msg
	 * @return
	 */
	public static String getHTMLEncode(String msg) {
		String result = msg.replaceAll("<", "&#60;");
		result = result.replaceAll(">", "&#62;");
		return result;
	}

	public static String randomUUID(String s) {
		return UUID.randomUUID().toString().replace("-", "");
	}

	/**
	 * 判断是否为数字
	 *
	 * @param str
	 * @return
	 */
	public static boolean isNumerStr(String str) {
		Matcher isNum = numberPattern.matcher(str);
		if (!isNum.matches()) {
			return false;
		}
		return true;
	}

	/**
	 * 判断是否全为数字字符串
	 *
	 * @param stringList
	 * @return
	 */
	public static boolean isAllNumerStr(List<String> stringList) {
		boolean isNumber = true;
		if (Utils.isNotEmpty(stringList)) {
			for (String str : stringList) {
				Matcher isNum = numberPattern.matcher(str);
				isNumber = isNumber && isNum.matches();
			}
		}
		return isNumber;
	}


	public static String getUUID() {
		return replaceUUID(UUID.randomUUID());
	}

	public static String replaceUUID(Object uuid) {
		return uuid.toString().replaceAll("\\-", "");
	}

	public static boolean isUUID(String uuid) {
		if (isNotEmpty(uuid) && uuid.length() == 32) {
			return true;
		}
		return false;
	}

	/**
	 * 校验是否是邮箱
	 *
	 * @param email
	 * @return
	 */
	public static boolean checkEmail(String email) {
		boolean flag = false;
		try {
			String check = "^[a-z0-9A-Z]+([-|_|\\.]?[a-z0-9A-Z]*)*@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
			Pattern regex = Pattern.compile(check);
			Matcher matcher = regex.matcher(email);
			flag = matcher.matches();
		} catch (Exception e) {
			e.printStackTrace();
			flag = false;
		}
		return flag;
	}

	public static boolean isEqualsTwoUUID(String s, String s1) {
		return isUUID(s) && isUUID(s1) && s.equals(s1);
	}


	public static String base64Encoder(String message) {
		try {
			byte[] encoderMessage = message.getBytes("utf-8");
			return ENCODER.encodeToString(encoderMessage);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return message;
	}

	public static String base64Encoder(byte[] message) {
		return ENCODER.encodeToString(message);
	}

	public static String base64Decoder(String message) {
		try {
			byte[] decoderMessage = message.getBytes("utf-8");
			return new String(DECODER.decode(decoderMessage), "utf-8");
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return message;
	}

	public static String getHttpServletRequestBody(HttpServletRequest request) throws IOException {
		String body = null;
		StringBuilder stringBuilder = new StringBuilder();
		BufferedReader bufferedReader = null;
		try {
			InputStream inputStream = request.getInputStream();
			if (inputStream != null) {
				bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
				char[] chars = new char[1024];
				int bytesRead = -1;
				while ((bytesRead = bufferedReader.read(chars)) > 0) {
					stringBuilder.append(chars, 0, bytesRead);
				}
			}
		} catch (IOException e) {
			throw e;
		} finally {
			if (null != bufferedReader) {
				bufferedReader.close();
			}
		}
		body = stringBuilder.toString();
		return body;
	}

	public static List<String> getListString(String vwlValue, String speratorChars) {
		List<String> stringList;
		try {
			stringList = Arrays.asList(StringUtils.split(vwlValue, speratorChars));
		} catch (Exception e) {
			stringList = new ArrayList<>();
		}
		return stringList;
	}

	public static boolean isDnsConnect(String dns) {
		String os = System.getProperty("os.name");
		boolean connect = false;
		Runtime runtime = Runtime.getRuntime();
		Process process;
		try {
			process = runtime.exec("ping" + (os.startsWith("Windows") ? "-n 2 " : "-c 2 ") + dns);
			InputStream is = process.getInputStream();
			InputStreamReader isr = new InputStreamReader(is);
			BufferedReader br = new BufferedReader(isr);
			String line = null;
			StringBuffer sb = new StringBuffer();
			while ((line = br.readLine()) != null) {
				sb.append(line);
			}
			is.close();
			isr.close();
			br.close();

			if (null != sb && !"".equals(sb.toString())) {
				if (sb.toString().toUpperCase().indexOf("TTL") > 0) {
					// 网络畅通
					connect = true;
				} else {
					// 网络不畅通
					connect = false;
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return connect;
	}

	/**
	 * 获取服务器Host
	 *
	 * @param request
	 * @return
	 */
	public static String getServerHost(HttpServletRequest request) {
		String basePath =
				request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
						+ request.getContextPath() + "/";
		return basePath;
	}

	public static String getClientUrl(HttpServletRequest request) {
		String urlPath = request.getHeader("Referer");
		if (isEmpty(urlPath)) {
			urlPath = getServerHost(request);
		}
		return urlPath;
	}

	/**
	 * 获取请求路径根信息
	 *
	 * @param request
	 * @return
	 */
	public static String getServerHostNoContextPath(HttpServletRequest request) {
		String basePath =
				request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort()
						+ "/";
		return basePath;
	}


	/**
	 * 保存文档图片到指定位置
	 *
	 * @param savePath
	 * @param file
	 * @param fileType
	 */
	public static void writeMultipartFileImage(String savePath, MultipartFile file,
	                                           String fileType) {
		File saveFile = new File(savePath);
		if (!saveFile.exists()) {
			saveFile.mkdirs();
		}
		try {
			BufferedImage read = ImageIO.read(file.getInputStream());
			ImageIO.write(read, fileType, saveFile);
		} catch (IOException e) {
			throw new RuntimeException("Save picture abnormal");
		}
	}

	/**
	 * 获取请求图片后缀
	 *
	 * @param request
	 * @return
	 */
	public static String getImgRequestSuffix(HttpServletRequest request) {
		String requestURI = request.getRequestURI();
		String[] split = requestURI.split("[.]");
		if (Utils.isNotEmpty(split)) {
			int length = split.length;
			String fileType = split[length - 1];
			if (uploadImgTypeCheck(fileType)) {
				return fileType;
			}
		}
		return null;
	}

	/**
	 * 上传图片校验
	 *
	 * @param fileType
	 * @return
	 */
	public static boolean uploadImgTypeCheck(String fileType) {
		if ("png".equalsIgnoreCase(fileType) || "jpg".equalsIgnoreCase(fileType) || "jpeg"
				.equalsIgnoreCase(fileType)) {
			return true;
		}
		return false;
	}

	/**
	 * 输出图片
	 *
	 * @param filepath
	 * @param fileName
	 * @param imgType
	 * @param request
	 * @param response
	 * @throws IOException
	 */
	public static void servletOutputStreamImgWrite(String filepath, String fileName,
	                                               String imgType, HttpServletRequest request,
	                                               HttpServletResponse response) throws IOException {
		File file = new File(filepath);
		if (!file.exists()) {
			return;
		}
		FileInputStream fileInputStream = new FileInputStream(file);
		BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
		//设置头
		response.reset();
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html;charset=utf-8");
		if ("jpg".equals(imgType)) {
			response.setContentType("image/jpeg");
		}
		if ("png".equals(imgType)) {
			response.setContentType("image/png");
		}
		ServletOutputStream os = response.getOutputStream();
		byte[] bytes = new byte[1024];
		int len = 0;
		while ((len = bufferedInputStream.read(bytes)) > 0) {
			os.write(bytes, 0, len);
		}
		os.close();
	}

	/**
	 * 获取三元表达式值
	 *
	 * @param isTrue
	 * @param officialValue
	 * @param alternativeValue
	 * @return
	 */
	public static String getTernary(boolean isTrue, String officialValue, String alternativeValue) {
		StringBuilder stringBuilder = new StringBuilder();
		return stringBuilder.append(isTrue ? officialValue : alternativeValue).toString();
	}


	/**
	 * List转String数组
	 *
	 * @param stringArrayList
	 * @return
	 */
	public static String[] convertList(List<String> stringArrayList) {
		String[] strings = null;
		if (isNotEmpty(stringArrayList)) {
			strings = stringArrayList.toArray(new String[stringArrayList.size()]);
		}
		return strings;
	}

	/**
	 * String数组转List
	 *
	 * @param strings
	 * @return
	 */
	public static List<String> convertString(String[] strings) {
		List<String> stringList = new ArrayList<>();
		if (Utils.isNotEmpty(strings)) {
			for (String value : strings) {
				stringList.add(value);
			}
		}
		return stringList;
	}

	/**
	 * 校验是否是中文
	 *
	 * @param str
	 * @return
	 */
	public static boolean isChinese(String str) {
		String regEx = "[\u4e00-\u9fa5]";
		Pattern pat = Pattern.compile(regEx);
		Matcher matcher = pat.matcher(str);
		boolean flg = false;
		if (matcher.find()) {
			flg = true;
		}

		return flg;
	}

	/**
	 * 去掉字符串的空白
	 *
	 * @param str
	 * @return
	 */
	public static String getStringNoBlank(String str) {
		if (str != null && !"".equals(str)) {
			Pattern p = Pattern.compile("\t|\r|\n");
			Matcher m = p.matcher(str);
			String strNoBlank = m.replaceAll("");
			return strNoBlank;
		} else {
			return str;
		}
	}

	/**
	 * 检查是否是本地回环地址
	 *
	 * @param url
	 * @return
	 */
	public static boolean isUriLocalhost(String url) {
		return isNotEmpty(url) && (url.contains("http://localhost") || url.contains("http://127.0.0.1")
				||
				url.contains("https://localhost") || url.contains("https://127.0.0.1"));
	}

	/**
	 * 获取Springboo的HttpServletRequest对象
	 *
	 * @return
	 */
	public static HttpServletRequest getRequest() {
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		return requestAttributes.getRequest();
	}

	/**
	 * 获取Springboo的HttpServletResponse对象
	 *
	 * @return
	 */
	public static HttpServletResponse getResponse() {
		ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
		return requestAttributes.getResponse();
	}

	/**
	 * 检查文件大小
	 *
	 * @param len  校验的文件长度
	 * @param size 文件大小
	 * @param unit 单位:B KB M G
	 * @return
	 */
	public static boolean checkFileSize(Long len, int size, String unit) {
		double fileSize = 0;
		if ("B".equals(unit.toUpperCase())) {
			fileSize = (double) len;
		} else if ("K".equals(unit.toUpperCase())) {
			fileSize = (double) len / 1024;
		} else if ("M".equals(unit.toUpperCase())) {
			fileSize = (double) len / 1048576;
		} else if ("G".equals(unit.toUpperCase())) {
			fileSize = (double) len / 1073741824;
		}
		if (fileSize > size) {
			return false;
		}
		return true;
	}

	/**
	 * 输出文件头字符
	 *
	 * @param src
	 * @return
	 */
	public static String bytesToHexString(byte[] src) {
		StringBuilder stringBuilder = new StringBuilder();
		if (src == null || src.length <= 0) {
			return null;
		}
		for (int i = 0; i < src.length; i++) {
			int v = src[i] & 0xFF;
			String hv = Integer.toHexString(v);
			if (hv.length() < 2) {
				stringBuilder.append(0);
			}
			stringBuilder.append(hv);
		}
		return stringBuilder.toString();
	}

	/**
	 * 获取随机命名后缀
	 *
	 * @return
	 */
	public static String generateSuffix() {
		// 获得当前时间
		DateFormat format = new SimpleDateFormat("yyyyMMddHHmmss");
		// 转换为字符串
		String formatDate = format.format(new Date());
		// 随机生成文件编号
		int random = new Random().nextInt(10000);
		return new StringBuffer().append(formatDate).append(
				random).toString();
	}

	/**
	 * Springboot获取根路径
	 *
	 * @return
	 */
	public static String getResourceBasePath() {
		File path = null;
		try {
			path = new File(ResourceUtils.getURL("classpath:").getPath());
		} catch (FileNotFoundException e) {
		}
		if (path == null || !path.exists()) {
			path = new File("");
		}
		String pathStr = path.getAbsolutePath();
		pathStr = pathStr.replace("\\target\\classes", "");
		return pathStr;
	}
}
